/**
 * $Author: francesco.benincasa $
 * $Date: 2007-03-08 13:07:20 +0100 (gio, 08 mar 2007) $
 * $Rev: 502 $
 */
package org.stars.database;

import java.util.HashMap;
import java.util.Map;
import java.sql.Connection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stars.util.diagnostic.DefaultStackTraceDiagnosticHandler;
import org.stars.util.diagnostic.StackTraceDiagnosticHandler;

/**
 * Gestisce i vari connection pool. Suo interno contiene un connection pool di
 * default.
 * 
 * @author Francesco Benincasa (908099)
 */
public class ConnectionPoolManager {

	// Private constructor prevents instantiation from other classes
	protected ConnectionPoolManager() {
		m_hConnectionPool = new HashMap<String, ConnectionPool>();
		defaultConnectionName = "";
		stackTraceDiagnosticHandler = new DefaultStackTraceDiagnosticHandler();
	}
	/**
	 * SingletonHolder is loaded on the first execution of
	 * Singleton.getInstance() or the first access to SingletonHolder.INSTANCE,
	 * not before.
	 */
	private static class ConnectionPoolManagerHolder {
		public static final ConnectionPoolManager INSTANCE = new ConnectionPoolManager();
	}

	public static ConnectionPoolManager getInstance() {
		return ConnectionPoolManagerHolder.INSTANCE;
	}
	
	/**
	 * @return the stackTraceDiagnosticHandler
	 */
	public StackTraceDiagnosticHandler getStackTraceDiagnosticHandler() {
		return stackTraceDiagnosticHandler;
	}

	/**
	 * gestore della visualizzazione dello stack trace.
	 */
	protected StackTraceDiagnosticHandler stackTraceDiagnosticHandler;

	/**
	 * @param stackTraceDiagnosticHandler
	 *            the stackTraceDiagnosticHandler to set
	 */
	public void setStackTraceDiagnosticHandler(StackTraceDiagnosticHandler value) {
		stackTraceDiagnosticHandler = value;
	}

	/**
	 * nome del connection pool di default
	 */
	protected String defaultConnectionName;

	/**
	 * Logger
	 * 
	 * @return logger
	 */
	protected static Log getLog() {
		return LogFactory.getLog(ConnectionPoolManager.class);
	}


	/**
	 * Restituisce l'hashtable di tutte le connessioni
	 * 
	 * @return hashtabl
	 */
	public Map<String, ConnectionPool> getAllConnectionPool() {
		return m_hConnectionPool;
	}

	/**
	 * Imposta tutti i connection pool.
	 * 
	 * @param params
	 *            hashtable contenente tutti i nuovi connection pool
	 */
	public void setAllConnectionPool(HashMap<String, ConnectionPool> params) {
		m_hConnectionPool = params;
	}

	/**
	 * Imposta un connection pool con un nome.
	 * 
	 * @param key
	 *            chiave con la quale verrà identificato il connection pool
	 * @param cp
	 *            connection pool
	 */
	public void setConnectionPool(String key, ConnectionPool cp) {

		key = key.toUpperCase();
		if (!m_hConnectionPool.containsKey(key)) {
			try {
				m_hConnectionPool.put(key, cp);
			} catch (Exception e) {
				Log log = getLog();
				log.error("Impossibile inserire il connection pool " + key + ": " + e.toString());
			}

		}
	}

	/**
	 * Recupera un connection pool in base alla sua chiave
	 * 
	 * @param key
	 * @return connection pool
	 */
	public ConnectionPool getConnectionPool(String key) {
		if (m_hConnectionPool.containsKey(key.toUpperCase()))
			return (ConnectionPool) m_hConnectionPool.get(key.toUpperCase());
		else
			return null;
	}

	/**
	 * Restituisce il connection pool di default.
	 * 
	 * @return connection pool di default o <code>null</code> nel caso esso non
	 *         esista.
	 */
	public ConnectionPool getDefaultConnectionPool() {
		return m_hConnectionPool.get(defaultConnectionName);
	}

	/**
	 * Recupera una connessione. La chiave è case insensitive.
	 * 
	 * @param key
	 *            nome del connection pool da utilizzare
	 * 
	 * @return connessione
	 * 
	 * @throws Exception
	 *             in caso di errore
	 */
	public Connection getConnection(String key) throws Exception {
		if (m_hConnectionPool.containsKey(key.toUpperCase())) {
			ConnectionPool cp = (ConnectionPool) m_hConnectionPool.get(key.toUpperCase());

			Connection conn = null;
			for (int i = 0; i < 3; i++) {
				try {
					conn = cp.getConnection();
					if (conn != null)
						break;
				} catch (Exception e) {
					Log log = getLog();
					log.error("Il connection pool " + key + " ha dei problemi per il collegamento. Tentativo " + i
							+ "\n<BR>" + e.toString());
				}
			}
			return conn;
		} else {
			throw new DatabaseException("Connection pool " + key + " non definito!!");
		}
	}

	/**
	 * Recupera la connessione di default. Salvo errori di configurazione,
	 * questa connessione è sempre definita.
	 * 
	 * @return connessione
	 * @throws Exception
	 *             in caso di errore
	 */
	public Connection getConnection() throws Exception {
		String key = defaultConnectionName;
		if (m_hConnectionPool.containsKey(key.toUpperCase())) {
			ConnectionPool cp = (ConnectionPool) m_hConnectionPool.get(key.toUpperCase());

			Connection conn = null;
			for (int i = 0; i < 3; i++) {
				try {
					conn = cp.getConnection();
					if (conn != null)
						break;
				} catch (Exception e) {
					Log log = getLog();
					log.error("Il connection pool " + key + " ha dei problemi per il collegamento. Tentativo " + i
							+ "\n<BR>" + e.toString());
				}
			}
			return conn;
		} else {
			throw new DatabaseException("Connection pool " + key + " non definito!!");
		}
	}

	/**
	 * hashtable dei connection pool.
	 */
	protected Map<String, ConnectionPool> m_hConnectionPool;

	/**
	 * Getter dell'attributo defaultConnectionName.
	 * 
	 * @return the defaultConnectionName
	 */
	public String getDefaultConnectionName() {
		return defaultConnectionName;
	}

	/**
	 * Setter dell'attributo defaultConnectionName.
	 * 
	 * @param defaultConnectionName
	 *            the defaultConnectionName to set
	 */
	public void setDefaultConnectionName(String defaultConnectionName) {
		this.defaultConnectionName = defaultConnectionName;
	}

}
